import type { CSSProperties, ExtractPropTypes } from "vue"

export type UniRouteType = {
  navigateTo: UniApp.NavigateToOptions
  navigateBack: UniApp.NavigateBackOptions
  switchTab: UniApp.SwitchTabOptions
  redirectTo: UniApp.RedirectToOptions
  reLaunch: UniApp.ReLaunchOptions
}

export namespace Parameter {
  export type JoinObject = {
    /**
     * 第一个字符
     * @默认 ?
     */
    prefix?: string
    /**
     * 拼接一组key、value的字符
     * @默认 &, key=value&key=value，=是joinSymbol
     */
    piece?: string
    /**
     * key和value拼接的字符，key?value
     * @默认 =, key=value
     */
    joinSymbol?: string
    /**
     * 是否过滤空值的key，即 undefined、null、空字符串
     * @默认 true
     */
    filterEmpty?: boolean
    /**
     * 需要拼接的对象
     */
    obj?: RecordObject
  }

  export type ToPage<T extends keyof UniRouteType> = {
    /**
     * 路由跳转方式
     * @默认 navigateBack
     */
    openType: T
    /** 跳转路由的参数 */
    params: UniRouteType[T] | string
    /** 在跳转路由后添加的 query 参数 */
    query: RecordObject | JoinObject
  }

  export type CopyData = {
    /**
     * 如果 content 的值是 object，那么会将此 object 扁平化(只会扁平化第一层对象)，
     * 此属性就是将key、value 拼接的字符串
     * @默认 =, key=value
     */
    joinSymbol?: string
    /**
    * 拼接全部组 key、value 的属性
    * @默认 &, key=value&key=value，=是joinSymbol
    */
    piece?: string
    /**
     * 复制成功的提示语
     */
    successText?: string | (() => string)
    /**
     * 复制失败的提示语
     */
    failText?: string | (() => string)
    /**
     * 需要复制的内容
     */
    content: string | number | boolean | RecordObject | (() => CopyData['content'])
  }

  export type FormatNumber = {
    /**
     * 需要转化的数字
     */
    num: number
    /**
     * 在第几位添加 separatory
     * @默认 3
     */
    digit?: number
    /**
     * 给第几位塞对应的字符
     * @默认 ,
     */
    separatory?: string
  }

  export type KeepDecimalPlaces = {
    /**
     * 需要截断的数字
     */
    num: number
    /**
     * 保留几位小数
     * @默认 2
     */
    digit?: number
    /**
     * 如果此属性有值，那么小数位不足的话，则使用该属性填充
     */
    fillSymbol?: string
  }
}

export type PublicComponentProps = Partial<{ style: CSSProperties; class: unknown }>

export namespace MergeUnion {
  /** 提取联合所有 key */
  type AllKeys<T> = T extends any ? keyof T : never

  /** 提取字段的类型（合并所有分支） */
  type FieldType<T, K extends PropertyKey> =
    T extends unknown ? K extends keyof T ? T[K] : never : never

  /** 字段是否在某些分支中是 undefined（表示本身可选） */
  type FieldIncludesUndefined<T, K extends PropertyKey> =
    undefined extends FieldType<T, K> ? true : false

  /** 真正决定某字段是必选还是可选 */
  type PickField<T, K extends PropertyKey> =
    FieldIncludesUndefined<T, K> extends true
    ? MergeOption<Exclude<FieldType<T, K>, undefined>> | undefined
    : MergeOption<FieldType<T, K>>

  export type MergeOption<T> =
    [T] extends [(...args: Array<infer A>) => infer R]
      ? (...args: Array<MergeOption<A>>) => MergeOption<R>
      : [T] extends [Array<infer U>]
        ? Array<MergeOption<U>>
        : [T] extends [object]
          ? { [K in AllKeys<T>]?: PickField<T, K> } 
            & { [K in keyof T]: PickField<T, K> }
          : T
}

export interface ExpandImportMetaEnv {
  /** 前端加密密钥 */
  VITE_PWD_ENC_KEY: string
  /** OAUTH2 短信客户端信息 */
  VITE_OAUTH2_MOBILE_CLIENT: string
  /** OAUTH2 密码模式客户端信息 */
  VITE_OAUTH2_PASSWORD_CLIENT: string
  /** 开发环境变量名 */
  VITE_MODE_DEV: 'development'
  /** 生产环境变量名 */
  VITE_MODE_PROD: 'production'
  VITE_USER_NODE_ENV: ExpandImportMetaEnv[keyof Pick<ExpandImportMetaEnv, 'VITE_MODE_DEV' | 'VITE_MODE_PROD'>]
  /** token 类型 */
  VITE_TOKEN_TYPE: string
  /** 请求前缀 */
  VITE_REQUEST_BASE_URL: string
  /** 后端请求地址 */
  VITE_REQUEST_URL: string
  /** 项目完整地址，从 D 盘开始 */
  VITE_ROOT_DIR: string
  VITE_CJS_IGNORE_WARNING: 'true' | (string & {})
  /** baseUrl 地址 */
  VITE_OUT_DIR: string
  /** 是否裁剪请求前缀 **/
  VITE_SLICE_REQUEST_BASE_URL: 'true' | 'false'
}

export namespace ExtractProps {
  /**
   * 提取组件的 props 的 key
   * @example
   *  I = true 时，存在 { default: any } 或 不存在 { required: true }，那么会将此 key 剔除
   *  I = false，时，存在 { required: true } 并且 不存在 { default: any }，那么会将此 key 剔除
   */
  type Keys<T, I = false> = keyof T extends infer K 
    ? K extends keyof T 
      ? T[K] extends { default: any } 
        ? I extends true ? never : K 
        : T[K] extends { required: true } 
          ? I extends true ? K : never
          : I extends true ? never : K
      : never
    : never
  type GetValue<T, K> = K extends keyof ExtractPropTypes<T> 
    ? ExtractPropTypes<T>[K] 
    : never
  export type Types<T extends RecordObject> = {
    [K in Keys<T>]?: GetValue<T, K>
  } & {
    [K in Keys<T, true>]: GetValue<T, K>
  } & PublicComponentProps
}

export namespace ExtrackExpose {
  export type Generic = abstract new (...args: any) => any
  export type NoGeneric = (...args: any[]) => any
  export type InstanceExpose<T extends { $props: object }> = Omit<T, 
      (keyof T['$props'] & {}) 
      | (keyof T extends infer K ? K extends `$${string}` ? K : never : never)
    >
  export type Types<T> = T extends NoGeneric 
    ? Parameters<NonNullable<ReturnType<T>['__ctx']>['expose']>[0]
    : T extends Generic ? InstanceExpose<InstanceType<T>> : never
}